//package com.itmck.mp;
//
//import cn.hutool.core.collection.CollectionUtil;
//import cn.hutool.core.util.ReflectUtil;
//import cn.hutool.core.util.StrUtil;
//import com.baomidou.mybatisplus.annotation.TableField;
//import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
//import com.baomidou.mybatisplus.core.toolkit.StringUtils;
//import com.baomidou.mybatisplus.core.toolkit.sql.SqlUtils;
//import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
//import com.itmck.mp.annotation.EncryptTransaction;
//import com.itmck.mp.annotation.SensitiveData;
//import com.itmck.util.DesEncryptThreeUtil;
//import lombok.extern.slf4j.Slf4j;
//import org.apache.ibatis.executor.Executor;
//import org.apache.ibatis.executor.parameter.ParameterHandler;
//import org.apache.ibatis.executor.resultset.ResultSetHandler;
//import org.apache.ibatis.executor.statement.StatementHandler;
//import org.apache.ibatis.mapping.*;
//import org.apache.ibatis.plugin.*;
//import org.apache.ibatis.reflection.MetaObject;
//import org.apache.ibatis.session.Configuration;
//import org.apache.ibatis.session.ResultHandler;
//import org.apache.ibatis.session.RowBounds;
//import org.apache.ibatis.type.TypeHandlerRegistry;
//import org.jetbrains.annotations.NotNull;
//import org.springframework.core.annotation.AnnotationUtils;
//import org.springframework.util.CollectionUtils;
//
//import javax.annotation.Resource;
//import java.lang.reflect.Field;
//import java.sql.Connection;
//import java.sql.SQLException;
//import java.sql.Statement;
//import java.text.DateFormat;
//import java.util.*;
//import java.util.stream.Collectors;
//
//
///**
// * 爱意随风起，风止意难平  ---网抑云
// * <p>
// * Create by M ChangKe  10:33
// * <p>
// * <p>
// * mybatis-plus 插件扩展
// * https://solon.noear.org/api/com/baomidou/mybatisplus/solon/plugins/inner/InnerInterceptor.html
// */
//@Slf4j
////@Component
//@Intercepts({
//        @Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class})
//})
//public class MyMpInterceptor implements InnerInterceptor, Interceptor {
//
//
//
//    @Resource
//    private DesEncryptFactory desEncryptFactory;
//
//    @Override
//    public Object intercept(Invocation invocation) throws Throwable {
//
//        //取出查询的结果
//        Object resultObject = invocation.proceed();
//        if (Objects.isNull(resultObject)) {
//            return null;
//        }
//        //基于selectList
//        if (resultObject instanceof ArrayList) {
//            ArrayList<?> resultList = (ArrayList<?>) resultObject;
//            if (!CollectionUtils.isEmpty(resultList) && needToDecrypt(resultList.get(0))) {
//                for (Object rel : resultList) {
//                    //逐一解密
//                    desEncryptFactory.decrypt(rel);
//                }
//            }
//            //基于selectOne
//        } else {
//            if (needToDecrypt(resultObject)) {
//                desEncryptFactory.decrypt(resultObject);
//            }
//        }
//        return resultObject;
//    }
//
//
//    /**
//     *
//     * 判断是否需要加密
//     * @param object
//     * @return
//     */
//    private boolean needToDecrypt(Object object) {
//        Class<?> objectClass = object.getClass();
//        SensitiveData sensitiveData = AnnotationUtils.findAnnotation(objectClass, SensitiveData.class);
//        return Objects.nonNull(sensitiveData);
//    }
//
//
//    @Override
//    public Object plugin(Object target) {
//        return Plugin.wrap(target, this);
//    }
//
//    @Override
//    public void setProperties(Properties properties) {
//
//    }
//
//    /**
//     * @param executor      Executor(可能是代理对象)
//     * @param ms            MappedStatement
//     * @param parameter     parameter
//     * @param rowBounds     rowBounds
//     * @param resultHandler resultHandler
//     * @param boundSql      boundSql
//     * @return
//     * @throws SQLException
//     */
//    @Override
//    public boolean willDoQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
//        return InnerInterceptor.super.willDoQuery(executor, ms, parameter, rowBounds, resultHandler, boundSql);
//    }
//
//    /**
//     * @param executor  Executor(可能是代理对象)
//     * @param ms        MappedStatement
//     * @param parameter parameter
//     * @return
//     * @throws SQLException
//     */
//    @Override
//    public boolean willDoUpdate(Executor executor, MappedStatement ms, Object parameter) throws SQLException {
//        return InnerInterceptor.super.willDoUpdate(executor, ms, parameter);
//    }
//
//    /**
//     * 操作前置处理 改改sql啥的
//     *
//     * @param executor      Executor(可能是代理对象)
//     * @param ms            MappedStatement
//     * @param parameter     parameter
//     * @param rowBounds     rowBounds
//     * @param resultHandler resultHandler
//     * @param boundSql      boundSql
//     */
//    @Override
//    public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
//
//        //获取配置信息
//        Configuration configuration = ms.getConfiguration();
//        //通过配置信息和BoundSql对象来生成带值得sql语句
//        String sql = geneSql(configuration, boundSql);
//        //打印sql语句
//        log.info("==> sql:" + sql);
//
//        String[] resultSets = ms.getResultSets();
//        System.out.println(resultSets);
//    }
//
//    /**
//     * 操作前置处理,可以针对参数进行修改，重新赋值，修改sql等
//     *
//     * @param sh                 StatementHandler(可能是代理对象)
//     * @param connection         Connection
//     * @param transactionTimeout transactionTimeout
//     */
//    @Override
//    public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) {
//
//        PluginUtils.MPStatementHandler mpSh = PluginUtils.mpStatementHandler(sh);
//        MappedStatement ms = mpSh.mappedStatement();
//        SqlCommandType sct = ms.getSqlCommandType();
//        if (sct != SqlCommandType.INSERT) {
//            System.out.println(sct);
//            return;
//        }
//        //获取到参数处理器
//        ParameterHandler parameterHandler = sh.getParameterHandler();
//        // 获取参数对像，即 mapper 中 paramsType 的实例
//        Object parameterObject = parameterHandler.getParameterObject();
//        if (null != parameterObject) {
//            // 校验该实例的类是否被@SensitiveData所注解
//            encode3Des(parameterObject);
//        }
//    }
//
//
//    /**
//     * 操作前置处理 改改sql啥的
//     *
//     * @param executor  Executor(可能是代理对象)
//     * @param ms        MappedStatement
//     * @param parameter parameter
//     */
//    @Override
//    public void beforeUpdate(Executor executor, MappedStatement ms, Object parameter) {
//
//
//        SqlCommandType sct = ms.getSqlCommandType();
//        if (sct != SqlCommandType.UPDATE) {
//            System.out.println(sct);
//            return;
//        }
//        Configuration configuration = ms.getConfiguration();
//        BoundSql boundSql = ms.getBoundSql(parameter);
//        //获取到要加密的字段
//        List<String> encodeKeys = getObjectEncryptKeys(ms);
//        //存放字段在sql中的位置
//        Map<Integer, String> propertyNameIndexMap = this.getSqlSegmentColumnIndex(boundSql.getSql())
//                .entrySet()
//                .stream()
//                .filter(ks -> encodeKeys.contains(ks.getValue()) || encodeKeys.contains(ks.getValue().toLowerCase()) || encodeKeys.contains(ks.getValue().toUpperCase()))
//                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
//
//        MetaObject metaObject = configuration.newMetaObject(parameter);//将映射文件的参数和对应的值返回，比如：id，name以及对应的值。
//        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();//获得映射的对象参数
//        for (int i = 0; i < parameterMappings.size(); i++) {
//            String propertyName = parameterMappings.get(i).getProperty();//获得属性名，如id,name等字符串
//            if (metaObject.hasGetter(propertyName)) {//检查该属性是否在metaObject中
//                Object obj = metaObject.getValue(propertyName);//如果在metaObject中，那么直接获取对应的值
//                if (CollectionUtil.isNotEmpty(encodeKeys) && CollectionUtil.isNotEmpty(propertyNameIndexMap)) {
//                    String value = propertyNameIndexMap.get(i);
//                    if (StringUtils.isNotBlank(value)) {
//                        metaObject.setValue(propertyName, DesEncryptThreeUtil.encode3Des((String) obj));//加密
//                    }
//                }
//            }
//        }
//        String s = geneSql(configuration, boundSql);
//        log.info("当前执行的sql:{}", s);
//    }
//
//    private Map<Integer, String> getSqlSegmentColumnIndex(String sql) {
//        sql = sql.replaceAll("\\s+", "");//获得带问号的sql语句
//        Map<Integer, String> propertyNameIndexMap = new HashMap<>();
//        String sqlSegment = sql.substring(sql.indexOf("SET") + "SET".length(), sql.indexOf(SqlUtils.WHERE))
//                .replace(SqlUtils.EQUALS, "")
//                .replace(SqlUtils.COMMA, "")
//                .trim();
//        String[] split = sqlSegment.split("\\?");
//        for (int i = 0; i < split.length; i++) {
//            propertyNameIndexMap.put(i, split[i].trim());
//        }
//        return propertyNameIndexMap;
//    }
//
//
//    /**
//     * 加密
//     *
//     * @param sensitiveObject
//     */
//    private void encode3Des(Object sensitiveObject) {
//        Class<?> sensitiveObjectClass = sensitiveObject.getClass();
//        SensitiveData sensitiveData = AnnotationUtils.findAnnotation(sensitiveObjectClass, SensitiveData.class);
//        if (Objects.nonNull(sensitiveData)) {
//            // 如果是被注解的类，则进行加密
//            // 取出当前当前类所有字段，传入加密方法
//            Field[] declaredFields = sensitiveObjectClass.getDeclaredFields();
//            for (Field field : declaredFields) {
//                //取出所有被EncryptTransaction注解的字段
//                EncryptTransaction encryptTransaction = field.getAnnotation(EncryptTransaction.class);
//                if (!Objects.isNull(encryptTransaction)) {
//                    field.setAccessible(true);
//                    //暂时只实现String类型的加密
//                    if (field.getType().equals(String.class)) {
//                        String value = (String) ReflectUtil.getFieldValue(sensitiveObject, field);
//                        if (StringUtils.isNotBlank(value)) {
//                            ReflectUtil.setFieldValue(sensitiveObject, field, DesEncryptThreeUtil.encode3Des(value));
//                        }
//                    }
//                }
//            }
//        }
//    }
//
//
//    /**
//     * 获取要加密的字段
//     *
//     * @param ms
//     * @return
//     */
//    @NotNull
//    private List<String> getObjectEncryptKeys(MappedStatement ms) {
//        ParameterMap parameterMap = ms.getParameterMap();
//        Class<?> type = parameterMap.getType();
//        //获取到需要加密的字段
//        List<String> kks = new ArrayList<>();
//        SensitiveData sensitiveData = AnnotationUtils.findAnnotation(type, SensitiveData.class);
//        // 如果是被注解的类，则进行加密
//        if (Objects.nonNull(sensitiveData)) {
//            Field[] declaredFields = type.getDeclaredFields();
//            for (Field field : declaredFields) {
//                //取出所有被EncryptTransaction注解的字段
//                EncryptTransaction encryptTransaction = field.getAnnotation(EncryptTransaction.class);
//                if (!Objects.isNull(encryptTransaction)) {
//                    //暂时只实现String类型的加密
//                    TableField tableField = field.getAnnotation(TableField.class);
//                    if (null != tableField) {
//                        String value = tableField.value();
//                        if (StringUtils.isNotBlank(value)) {
//                            kks.add(tableField.value());
//                        }
//                    } else {
//                        //驼峰转下划线
//                        kks.add(StrUtil.toUnderlineCase(field.getName()));
//                    }
//
//                }
//            }
//        }
//        return kks;
//    }
//
//
//    /**
//     * 生成对应的带有值得sql语句
//     *
//     * @param configuration
//     * @param boundSql
//     * @return
//     */
//    public static String geneSql(Configuration configuration, BoundSql boundSql) {
//
//        //获得参数对象
//        Object parameterObject = boundSql.getParameterObject();
//        //获得映射的对象参数
//        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
//        String sql = boundSql.getSql().replaceAll("\\s+", " ");//获得带问号的sql语句
//        //如果参数个数大于0且参数对象不为空，说明该sql语句是带有条件的
//        if (parameterMappings.size() > 0 && parameterObject != null) {
//            TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
//            //检查该参数是否是一个参数
//            if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
//                //getParameterValue用于返回是否带有单引号的字符串，如果是字符串则加上单引号
//                sql = sql.replaceFirst("\\?", getParameterValue(parameterObject));//如果是一个参数则只替换一次，将问号直接替换成值
//
//            } else {
//                MetaObject metaObject = configuration.newMetaObject(parameterObject);//将映射文件的参数和对应的值返回，比如：id，name以及对应的值。
//                for (ParameterMapping parameterMapping : parameterMappings) {//遍历参数，如:id,name等
//                    String propertyName = parameterMapping.getProperty();//获得属性名，如id,name等字符串
//                    if (metaObject.hasGetter(propertyName)) {//检查该属性是否在metaObject中
//                        Object obj = metaObject.getValue(propertyName);//如果在metaObject中，那么直接获取对应的值
//                        sql = sql.replaceFirst("\\?", getParameterValue(obj));//然后将问号?替换成对应的值。
//                    } else if (boundSql.hasAdditionalParameter(propertyName)) {
//                        Object obj = boundSql.getAdditionalParameter(propertyName);
//                        sql = sql.replaceFirst("\\?", getParameterValue(obj));
//                    }
//                }
//            }
//        }
//        return sql;//最后将sql语句返回
//    }
//
//    /**
//     * 如果是字符串对象则加上单引号返回，如果是日期则也需要转换成字符串形式，如果是其他则直接转换成字符串返回。
//     *
//     * @param obj
//     * @return
//     */
//    private static String getParameterValue(Object obj) {
//        String value;
//        if (obj instanceof String) {
//            value = "'" + obj + "'";
//        } else if (obj instanceof Date) {
//            DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.CHINA);
//            value = "'" + formatter.format(obj) + "'";
//        } else {
//            if (obj != null) {
//                value = obj.toString();
//            } else {
//                value = "";
//            }
//
//        }
//        return value;
//    }
//}
